package studentUeb;

 
import java.util.*;
import java.io.Serializable;
import htw.student.*;
import studentUeb.*;
import htw.list.*;
import studentUeb.exceptions.*;
import htw.exceptions.*;
/** 
 * Implementierung einer einfach-verketteten linearen StudentListe
 * 
 * @author Andreas Dier, Steffen Schuhmann
 * @version 1.2
 */
public class StudentList extends htw.list.List {


    private int size;
    private ListElement first;
    private ListElement last;

    /** 
     * Leere Liste erzeugen  
     */
    public StudentList() {
        first = null;
        last = null;
        size = 0;
    }

    /**
     * Ein Objekt an einer bestimmten Stelle hinzufuegen
     *
     * @param index Der Index vor dem eingefuegt werden soll (0 <= index <= size)
     * @param o der einzufuegende Student
     * @exception IndexOutOfBoundsException falls index nicht im vorgegebenen Intervall ist
     */
    public void add(int index, Student o) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("index:" + index +
                                                ", size:" + size);
        if(!contains(o.getMatrikelNr()))
		{
			if (index == size)
				addLast(o);
			else if (index == 0)
				addFirst(o);
			else {
				// beginne beim erstem Element
				ListElement le = first;
				// gehe bis zum Element index-1
				for (int i = 0; i < index-1; i++)
					le = le.next;
				// dort anhaengen
				ListElement tmp = new ListElement(o, le.next);
				le.next = tmp;
				size++;
			}
		}
    }

    /**
     * Element am Anfang der Liste einfuegen 
     *
     * @param o einzufuegendes Element
     */
    public void addFirst(Student o) {
        ListElement tmp = new ListElement(o, first);
        first = tmp;
        size++;
        if (last == null)
           last = first;
    }

    /** 
     * Element am Ende der Liste anhaengen   
     *
     * @param o einzufuegendes Element
     */
    public void addLast(Student o) {
        if (last == null)
            addFirst(o);
        else {
            last.next = new ListElement(o, null);
            last = last.next;
            size++;
        }
    }

    /** 
     * Komplette Liste loeschen        
     */
    public void clear() {
        first = null;
        last = null;
        size = 0;
    }

    /** 
     * Enthaelt die Liste den Studenten
     *
     * @param o einzufuegendes Element
     * @return true, falls das Objekt in der Liste vorkommt, false sonst
     */
    public boolean contains(Student o) {
        return indexOf(o) != -1;
    }
	
	/** 
     * Enthaelt die Liste einen Studenten mit der Matrikelnummer
     *
     * @param o einzufuegendes Element
     * @return true, falls das Objekt in der Liste vorkommt, false sonst
     */
    public boolean contains(int matrNr) {
        return indexOf(matrNr) != -1;
    }

    /** 
     *  Ein Iteratorobjekt zurueckgeben, mit dem ueber die Liste
     *  iteriert werden kann
     *  @return Iteratorobjekt
     */
    public Iterator iterator() {
        return new ListItr(first);
    }

    /** 
     * Objekt an der Stelle index zurueckgeben 
     * 
     * @param index Der Index dessen zugehoeriges Objekt zurueckzugeben ist (0 <= index < size)
     * @return das gefundene Objekt
     * @exception IndexOutOfBoundsException falls index nicht im vorgegebenen Intervall ist
     */
    public Student get(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("index:" + index +
                                                ", size:" + size);
        ListElement le = first;
        int i = 0;
        while (i < index) {
            le = le.next;
            i++;
        }
        return (Student) le.data;
    }

    /** 
     * Das erste Element zurueckgeben 
     * 
     * @return der erste Student
     * @exception NoSuchElementException falls es die Liste leer ist
     */
    public Student getFirst() {
        if (size == 0)
            throw new NoSuchElementException();
        return (Student) first.data;
    }

    /** 
     * Das letzte Element zurueckgeben 
     * 
     * @return der letzte Student
     * @exception NoSuchElementException falls es die Liste leer ist
     */
    public Student getLast() {
        if (size == 0)
            throw new NoSuchElementException();
        return (Student) last.data;
    }

    /** 
     * An welcher Stelle steht der Student?     
     * @param o der zu suchende Student
     * @return den Index des Studenten oder -1
     */
    public int indexOf(Student o) {
        int index = 0;
        ListElement le = first;
        while (le != null) {
            if (le.data.equals(o))
                return index;
            le = le.next;
            index++;
        }
        return -1;
     }
	
	 /** 
     * An welcher Stelle steht der Student mit der matrikelnummer?     
     * @param o der zu suchende Student
     * @return den Index des Studenten oder -1
     */
    public int indexOf(int matrNr) {
        int index = 0;
        ListElement le = first;
        while (le != null) {
            if (((Student)le.data).getMatrikelNr()==matrNr)
                return index;
            le = le.next;
            index++;
        }
        return -1;
     }

    /** 
     * Entferne Element mit einem bestimmten Inhalt
     * 
     * @param o der zu entfernende Student
     */
    public void remove(Student o) {
        if (size == 0 || o == null ) return;
        if (first.data.equals(o))
            removeFirst();
        else if (last.data.equals(o))
            removeLast();
        else {
            ListElement le = first;
            // gehe bis zum Element vor dem gesuchten
            while (le.next != last) {
                if (le.next.data.equals(o)) {
                    le.next = le.next.next;
                    size--;
                    break;
                }
                le = le.next;
            }
        }
    }
	
	/** 
     * Entferne Element mit einem bestimmten Inhalt
     * 
     * @param o der zu entfernende Student
     */
    public void removeMatr(int matrNr) {
        if (size == 0 ) return;
        if (((Student) first.data).getMatrikelNr() == matrNr)
            removeFirst();
        else if (((Student) last.data).getMatrikelNr() == matrNr)
            removeLast();
        else {
            ListElement le = first;
            // gehe bis zum Element vor dem gesuchten
            while (le.next != last) {
                if (((Student) le.next.data).getMatrikelNr() == matrNr) {
                    le.next = le.next.next;
                    size--;
                    break;
                }
                le = le.next;
            }
        }
    }
	
    /**
     * Loesche Element an der Stelle index
     * 
     * @param index Der Index dessen zugehoeriger Student zu loeschen ist (0 <= index < size)
     * @exception IndexOutOfBoundsException falls index nicht im vorgegebenen Intervall ist
     */
    public void remove(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("index:" + index +
                                                ", size:" + size);
        if (index == size-1)
            removeLast();
        else if (index == 0)
            removeFirst();
        else {
            // beginne beim erstem Element
            ListElement le = first;
            // gehe bis zum Element index-1
            for (int i = 0; i < index-1; i++)
                le = le.next;
            // Das i-te Element einfach ueberspringen
            le.next = le.next.next;
            size--;
        }
    }

    /**
     * Das erste Element loeschen
     */
    public void removeFirst() {
        if (size == 0) return;
        if (first == last) {
            first = null;
            last = null;
        } else {
            first = first.next;
        }
        size--;
    }

    /**
     * Das letzte Element loeschen
     */
    public void removeLast() {
        if (size == 0) return;	// leere Liste ?
        if (first == last) {    // Nur ein Listenelement
            first = null;
            last = null;
        } else {
            ListElement tmp = first;
            // vorletztes Element suchen
            while (tmp.next != last)
                tmp = tmp.next;
            last = tmp;// tmp zeigt nun auf das vorletzte Listenelement
            last.next = null;
        }
        size--;
    }

    /** 
     * Wert eines Elementes veraendern, den alten Wert zurueckgeben 
     * 
     * @param index Der Index des zu aendernden Studenten (0 <= index < size)
     * @param o der neue Student
     * @exception IndexOutOfBoundsException falls index nicht im vorgegebenen Intervall ist
     */
    public Student set(int index, Student o) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("index:" + index +
                                                ", size:" + size);
        // beginne beim erstem Element
        ListElement le = first;
        // gehe bis zum Element index
        for (int i = 0; i < index; i++)
                le = le.next;
        Student alterWert = (Student) le.data;
        le.data = o;
        return alterWert;
    }

    /**
     * @return Anzahl der Elemente
     */
    public int size() {
        return size;
    }

    /**
     * @return die Liste als zeichenkette
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
//         ListElement le = first;
//         while (le != null) {
//             sb.append(" -> ").append(le.data.toString());
//             le = le.next;
//         }
        for (Object o : this) 
             sb.append(" -> ").append(o);
           
        return sb.toString();
    }
	
	/**
     * Kopiert die aktuelle Liste und gibt die Kopie zurueck
     * 
     * @return gibt die kopierte Liste zurueck
     * @exception NoListException falls liste null ist
     */
    public StudentList clone()
    {
         
        StudentList listcopy;
        listcopy = new StudentList();
        
        for( int i = 0; i < size(); i++ )
        {
            (listcopy).addLast(get(i));
        }
        return listcopy;
    }
	
	/**
     * Eine Liste wird an die vorhandene angehaengt
     * 
     * @param anzufuegende Liste
     * @exception NoListException falls liste null ist
     */
    public void append(htw.list.List l) throws htw.exceptions.NoListException
    {
        if( size == 0 )
            throw new htw.exceptions.NoListException("Liste wurde noch nicht angelegt");
        
        if( l.size() == 0 )
            throw new htw.exceptions.NoListException("Anzufuegende Liste wurde noch nicht erstellt");
        
        for( int i =0; i < l.size(); i++ )
        {
            addLast( l.get(i));
        }
    }
	
	/**
     * Vergleicht zwei Listen auf inhaltlische gleichheit
     * 
     * @param zu pruefende Liste
     * @return gibt false aus, wenn die Listen nicht gleich sind
     * @exception NoListException falls liste null ist
     */
	public boolean equals(htw.list.List l) throws htw.exceptions.NoListException
    {
        if( size == 0 )
            throw new htw.exceptions.NoListException("Liste wurde noch nicht angelegt");
        
        if( l.size() == 0 )
            throw new htw.exceptions.NoListException("Anzufuegende Liste wurde noch nicht erstellt");
        
        
        for( int i =0; i < l.size(); i++ )
        {
            if( indexOf(l.get(i)) == -1) 
                return false;         
        }
        return true;
    }
}


/**
 *  Listenelement, zeigt auf ein Inhaltsobjekt und auf das
 *  naechste Listenelement
 */
class ListElement {
    protected Object data;
    protected ListElement next;

    /**
     * Der Konstruktor ist protected, da er nur von der Liste verwendet werden soll.
     *
     * @param o Inhalt des Elementes
     * @param le Vorgabe fuer die next-Referenz
     */
    protected ListElement (Object o, ListElement le) {
        data = o;
        next = le;
    }
}

/**
 * Implementierung eines Iterator-Typs fuer die Liste
 */
class ListItr implements Iterator {
    private ListElement le;
    /**
     * Ein Iteratorobjekt erzeugen
     *
     * @param start das Listenelement mit dem gestartet werden soll
     */
    public ListItr(ListElement start) {
        le = start;
    }
    /**
     * Gibt es ein nachfolgendes mElement?
     *
     * @return true, falls ja andernfalls false
     */
    public boolean hasNext() {
        return le != null;
    }

    /**
     * Gehe zum naechsten Element weiter
     *
     * @return Referenz auf das naechste Element
     * @exception NoSuchElementException falls es kein naechstes Element mehr gibt
     */
    public Object next() throws NoSuchElementException {
        if (le == null)
            return new NoSuchElementException();
        Object o = le.data;
        le = le.next;
        return  o;
    }
    /**
     * Diese Methode ist nicht implementiert
     * 
     * @exception UnsupportedOperationException falls die Methode aufgerufen wird
     */
    public void remove() throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }
}


